﻿using UnityEngine;
using System.Collections;

public class TestScript3 : MonoBehaviour {

	public AnimationCurve animationCurve;
	public AnimationCurve animationCurve1;
	public AnimationCurve animationCurve2;
	public Gradient gradient;
	public Gradient gradient1;

	public void OnDrawGizmos ()
	{
		if (animationCurve != null && animationCurve.length != 0) {
			for (float t = -3f; t <= 3f; t += 0.01f) {
				float v = Evaluate(animationCurve, t);
				if (t >= animationCurve.keys[0].time && t <= animationCurve.keys[animationCurve.keys.Length - 1].time) {
					Gizmos.color = Color.red;
				} else {
					Gizmos.color = Color.gray;
				}
				Gizmos.DrawSphere(new Vector3(t, v, 0), 0.1f);
			}
		}
		if (animationCurve1 != null && animationCurve1.length != 0) {
			for (float t = -3f; t <= 3f; t += 0.01f) {
				float v = Evaluate(animationCurve1, t);
				Gizmos.color = Evaluate(gradient, (t + 3f) / 6f);
				Gizmos.DrawSphere(new Vector3(t, v, 0), 0.1f);
			}
		}
		if (animationCurve2 != null && animationCurve2.length != 0) {
			Gizmos.color = Color.green;
			for (float t = -3f; t <= 3f; t += 0.01f) {
				float v = Evaluate(animationCurve2, t);
				Gizmos.color = Evaluate(gradient1, (t + 3f) / 6f);
				Gizmos.DrawSphere(new Vector3(t, v, 0), 0.1f);
			}
		}
	}

	#region - AnimationCurve
	protected float Evaluate (AnimationCurve animationCurve, float t)
	{
		if (animationCurve.keys.Length == 0)
			return 0f;

		float t0 = animationCurve.keys [0].time;
		float t1 = animationCurve.keys [animationCurve.keys.Length - 1].time;
		if (t < t0) {
			if (animationCurve.preWrapMode == WrapMode.ClampForever) {
				return animationCurve.keys [0].value;
			} else if (animationCurve.preWrapMode == WrapMode.Loop) {
				return _EvaluateLoop (animationCurve, t, t0, t1);
			} else if (animationCurve.preWrapMode == WrapMode.PingPong) {
				return _EvaluatePingPong (animationCurve, t, t0, t1);
			}
		} else if (t > t1) {
			if (animationCurve.postWrapMode == WrapMode.ClampForever) {
				return animationCurve.keys [animationCurve.keys.Length - 1].value;
			} else if (animationCurve.postWrapMode == WrapMode.Loop) {
				return _EvaluateLoop (animationCurve, t, t0, t1);
			} else if (animationCurve.postWrapMode == WrapMode.PingPong) {
				return _EvaluatePingPong (animationCurve, t, t0, t1);
			}
		} else {
			return _EvaluateCurve (animationCurve, t);
		}
		return 0f;
	}

	protected float _EvaluateLoop (AnimationCurve animationCurve, float t, float t0, float t1) 
	{
		float T = t - t0;
		T = T / (t1 - t0);
		T = T - (int)T;

		if (T < 0f)
			T = 1f - Mathf.Abs(T);
		
		return _EvaluateCurve(animationCurve, t0 + T * (t1 - t0));
	}

	protected float _EvaluatePingPong (AnimationCurve animationCurve, float t, float t0, float t1) 
	{
		float T = t - t0;
		T = T / (t1 - t0);
		int I = (int)Mathf.Abs (Mathf.Ceil(T));
		T = T - (int)T;
		float T_ = T;

		if (I % 2 == 0)
			T = 1f - Mathf.Abs(T);

		if (T_ < 0f)
			T = 1f - Mathf.Abs(T);

		return _EvaluateCurve(animationCurve, t0 + T * (t1 - t0));
	}

	protected float _EvaluateCurve (AnimationCurve animationCurve, float t)
	{
		Keyframe[] keys = animationCurve.keys;
		for (int i = 0; i < keys.Length - 1; i++) {
			Keyframe k0 = keys[i]; 
			Keyframe k1 = keys[i + 1];
			if (t >= k0.time && t <= k1.time) {
				if (float.IsInfinity(k0.outTangent) || float.IsInfinity(k1.inTangent)) {
					return k0.value;
				} else {
					return _EvaluateStep(k0, k1, (t - k0.time) / (k1.time - k0.time));
				}
			}
		}
		return 0f;
	}

	protected float _EvaluateStep (Keyframe k0, Keyframe k1, float t)
	{
		float dt = k1.time - k0.time;
		
		float m0 = k0.outTangent * dt;
		float m1 = k1.inTangent * dt;
		
		float t2 = t * t;
		float t3 = t2 * t;
		
		float a = 2f * t3 - 3f * t2 + 1f;
		float b = t3 - 2f * t2 + t;
		float c = t3 - t2;
		float d = -2f * t3 + 3f * t2;
		
		return a * k0.value + b * m0 + c * m1 + d * k1.value;
	}
	#endregion

	#region - Gradient
	protected Color Evaluate (Gradient gradient, float t)
	{
		Color c = Color.black;
		float a = 1f;

		if (gradient.colorKeys.Length != 0) {
			if (gradient.colorKeys.Length == 1 || t < gradient.colorKeys[0].time) {
				c = gradient.colorKeys[0].color;
			} else if (t > gradient.colorKeys[gradient.colorKeys.Length - 1].time) {
				c = gradient.colorKeys[gradient.colorKeys.Length - 1].color;
			} else {
			    for (int i = 0; i < gradient.colorKeys.Length - 1; i++) {
					GradientColorKey k0 = gradient.colorKeys[i];
					GradientColorKey k1 = gradient.colorKeys[i + 1];
					if (t >= k0.time && t <= k1.time) {
						c = Color.Lerp(k0.color, k1.color, (t - k0.time) / (k1.time - k0.time));
					}
				}
			}
		}

		if (gradient.alphaKeys.Length != 0) {
			if (gradient.alphaKeys.Length == 1 || t < gradient.alphaKeys[0].time) {
				a = gradient.alphaKeys[0].alpha;
			} else if (t > gradient.alphaKeys[gradient.alphaKeys.Length - 1].time) {
				a = gradient.alphaKeys[gradient.alphaKeys.Length - 1].alpha;
			} else {
				for (int i = 0; i < gradient.alphaKeys.Length - 1; i++) {
					GradientAlphaKey k0 = gradient.alphaKeys[i];
					GradientAlphaKey k1 = gradient.alphaKeys[i + 1];
					if (t >= k0.time && t <= k1.time) {
						a = Mathf.Lerp(k0.alpha, k1.alpha, (t - k0.time) / (k1.time - k0.time));
					}
				}
			}
		}

		return new Color(c.r, c.g, c.b, a);
	}
	#endregion

}
